
// ===============================================================================================================
// -*- C++ -*-
//
// GfxLib.cpp - Graphics rendering code.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <GfxLib.hpp>

namespace GfxLib {

// Global data:
ScreenInfo screen = {0,0};
int numEnabledLights = 0;
ShaderProgram * defaultShader = 0;
Material * defaultMaterial = 0;
float fov, zNear, zFar;

bool Initialize(const char * cfgFileName)
{
	assert(cfgFileName != 0);
	CommLib::DbgPrintf(PRINT_MSG, "GFX Lib initializing...");

	// Open the config file:

	CommLib::INIFile gfxConfig;
	if (!gfxConfig.Read(cfgFileName))
	{
		CommLib::DbgPrintf(PRINT_ERROR, "Error opening GFX config... Unable to continue");
		return (false);
	}

	const CommLib::INISection * iniSect = gfxConfig.GetSection("GFX Config");

	if (!iniSect)
	{
		CommLib::DbgPrintf(PRINT_ERROR, "Missing [GFX Config] section in INI file...");
		return (false);
	}

	// Get the INI vars...
	std::string windowTitle;
	MathLib::Vec3f bgColor;
	int maxTextures;

	iniSect->GetInteger("ScreenWidth", screen.width);
	iniSect->GetInteger("ScreenHeight", screen.height);
	iniSect->GetFloat("FOV", fov);
	iniSect->GetFloat("ZNear", zNear);
	iniSect->GetFloat("ZFar", zFar);
	iniSect->GetVector("BgColor", bgColor);
	iniSect->GetString("WindowTitle", windowTitle);
	iniSect->GetInteger("MaxTextures", maxTextures);

	// Init Glut, OpenGL:

	int argc = 1;
	char * argv[1] = { "Long Live John Carmack !" };
	glutInit(&argc, argv); // This will fail if we pass null pointers to it!
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(screen.width, screen.height);
	glutInitWindowPosition(0,0);

	// Create Glut window:
	int winId = glutCreateWindow(windowTitle.c_str());
	glutSetWindow(winId);

	glewInit();
	CommLib::DbgPrintf(PRINT_MSG, "OpenGL, GLUT and GLEW initialized");
	CommLib::DbgPrintf(PRINT_MSG, "Using OpenGL %s", glGetString(GL_VERSION));
	CommLib::DbgPrintf(PRINT_MSG, "Using GLEW %s", glewGetString(GLEW_VERSION));

	// Set some default GL states:
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glClearColor(bgColor.x, bgColor.y, bgColor.z, 1.0f);

	SetViewPort(screen.width, screen.height, fov, zNear, zFar);

	// Init shared resources:

	InitTextures(maxTextures);

	const char * shaderFiles[2] = { "UniLibrary/GfxLib/BlinnPhong.vert", "UniLibrary/GfxLib/BlinnPhong.frag" };
	defaultShader = new GfxLib::ShaderProgram(shaderFiles, 2);
	CommLib::DbgPrintf(PRINT_MSG, "Created default Blinn-Phong lighting shader...");

	defaultMaterial = new Material;
	CommLib::DbgPrintf(PRINT_MSG, "Created a default material...");

	return (true);
}

void EnableLights(int numLights, const MathLib::Vec3f * lightPositions)
{
	int i;

	if (numLights > 0)
	{
		assert(lightPositions != 0 && numLights <= GL_MAX_LIGHTS);

		for (i = 0; i < numLights; ++i)
		{
			glLightfv(GL_LIGHT0 + i, GL_POSITION, lightPositions[i].v);
			glEnable(GL_LIGHT0 + i);

			CommLib::DbgPrintf(PRINT_MSG, "Enabled GL_LIGHT%d", i);
		}

		glEnable(GL_LIGHTING);
		numEnabledLights = numLights;
	}
	else
	{
		for (i = 0; i < numEnabledLights; ++i)
		{
			glDisable(GL_LIGHT0 + i);
			CommLib::DbgPrintf(PRINT_MSG, "Disabled GL_LIGHT%d", i);
		}

		glDisable(GL_LIGHTING);
		numEnabledLights = 0;

		CommLib::DbgPrintf(PRINT_MSG, "All lights disabled");
	}
}

void SetViewPort(int w, int h, float fov, float zNear, float zFar)
{
	GfxLib::screen.width = w;
	GfxLib::screen.height = h;
	GfxLib::fov = fov;
	GfxLib::zNear = zNear;
	GfxLib::zFar = zFar;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glViewport(0, 0, screen.width, screen.height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(GfxLib::fov, (static_cast<double>(screen.width) / static_cast<double>(screen.height)), GfxLib::zNear, GfxLib::zFar);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glutPostRedisplay();
}

void SetCallbacks(void (*render)(void), void (*reshapeWindow)(int width, int height),
				  void (*mouse)(int button, int state, int x, int y), void (*mouseMotion)(int x, int y),
				  void (*keyboard)(unsigned char key, int x, int y), void (*keyboardUp)(unsigned char key, int x, int y))
{
	if (render)
	{
		glutDisplayFunc(render);
		glutIdleFunc(render);
	}
	if (reshapeWindow)
	{
		glutReshapeFunc(reshapeWindow);
	}
	if (mouse)
	{
		glutMouseFunc(mouse);
	}
	if (mouseMotion)
	{
		glutMotionFunc(mouseMotion);
	}
	if (keyboard)
	{
		glutKeyboardFunc(keyboard);
	}
	if (keyboardUp)
	{
		glutKeyboardUpFunc(keyboardUp);
	}
}

void RunRenderingLoop(void)
{
	CommLib::DbgPrintf(PRINT_MSG, "Entering main loop...");
	glutMainLoop();
}

void Terminate(void)
{
	CleanupTextures();
	CleanupFonts();

	delete defaultShader;
	delete defaultMaterial;

	CommLib::DbgPrintf(PRINT_MSG, "GFX Lib terminated...");
}

}; // namespace GfxLib {}